//
//  NSObject+KVO.m
//  KVO
//
//  Created by jun on 2021/2/3.
//

#import "NSObject+KVO.h"
#import <objc/message.h>

static NSString *const kKCKVOPrefix = @"KCKVO_";
static NSString *const kKCKVOAssiociateKey = @"kKCKVO_AssiociateKey";

@implementation NSObject (KVO)

- (void)lg_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath{
    // 1: 是否有setter方法
    // id superClassName = object_getClassName(self); // person
    // setName
    NSString *setterMethodName = setterForGetter(keyPath); // setName:
    SEL setterSel = NSSelectorFromString(setterMethodName);
    // method
    Method method = class_getInstanceMethod([self class], setterSel);// runtime 1900009931
    if (!method) {
        @throw [[NSException alloc] initWithName:NSExtensionItemAttachmentsKey reason:@"没有setter方法" userInfo:nil];
    }
    
    //2: 动态生成子类
    Class childClass = [self creatChildClassWithKeypath:keyPath];
    if (!childClass) {
        NSLog(@"创建失败");
    }
    // 3.0 消息转发
    // observer
    // 关联对象   //用于set方法中取出这个对象，将消息发送到这个歌对象的方法中
    objc_setAssociatedObject(self, (__bridge const void * _Nonnull)(kKCKVOAssiociateKey), observer, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

#pragma mark - 动态创建子类

- (Class)creatChildClassWithKeypath:(NSString *)keyPath{
    
    NSString *oldClassName   = NSStringFromClass([self class]);//person
    NSString *childClassName = [NSString stringWithFormat:@"%@%@",kKCKVOPrefix,oldClassName];
    
    //2: 动态生成子类
    //2.1 申请类
    Class childClass = objc_allocateClassPair([self class], childClassName.UTF8String, 0);
    //2.2 注册类
    objc_registerClassPair(childClass);
    //2.3 添class
    SEL classSel = NSSelectorFromString(@"class");
    Method classMethod = class_getClassMethod([self class], classSel);
    const char *classType = method_getTypeEncoding(classMethod);
    class_addMethod(childClass, classSel, (IMP)lg_Class, classType);
    //2.4 setter : setName:
    SEL setterSel = NSSelectorFromString(setterForGetter(keyPath));
    Method setterMethod = class_getClassMethod([self class], setterSel);
    const char *setterType = method_getTypeEncoding(setterMethod);
    class_addMethod(childClass, setterSel, (IMP)lg_setter, setterType);
    //2.5 isa 指向
    object_setClass(self, childClass);
    return childClass;
}

/**
 判断是否存在该方法
 */
- (BOOL)hasSeletor:(SEL)selector{
    
    Class observedClass = object_getClass(self);
    unsigned int methodCount = 0;
    //得到一堆方法的名字列表  //class_copyIvarList 实例变量  //class_copyPropertyList 得到所有属性名字
    Method *methodList = class_copyMethodList(observedClass, &methodCount);
    
    for (int i = 0; i<methodCount; i++) {
        SEL sel = method_getName(methodList[i]);
        if (selector == sel) {
            free(methodList);
            return YES;
        }
    }
    free(methodList);
    return NO;
}

#pragma mark - 函数区域
static Class lg_Class(id self,SEL _cmd){
    return class_getSuperclass(object_getClass(self));
}

static void lg_setter(id self,SEL _cmd,id value){

    NSLog(@"lg_setter - %@",value);
    
    id observer = objc_getAssociatedObject(self, (__bridge const void * _Nonnull)(kKCKVOAssiociateKey));
    SEL handlSEL = @selector(lg_observeValueForKeyPath: ofObject:newValue:);
    NSString *keypath = getterForSetter(NSStringFromSelector(_cmd));
//    objc_msgSend();
    //报错时，buildSettings》Enable strict checking of objc_msgSend calls
    objc_msgSend(observer,handlSEL,keypath,self,value);
//    objc_msgSend(observer,handlSEL,keypath,self,value);
    //取出之前关联的对象，将消息发送到这个歌对象的方法中
//    [observer performSelector:@selector(lg_observeValueForKeyPath: ofObject:newValue:) withObject:self afterDelay:0];
}

#pragma mark - 从get方法获取set方法的名称 name ===>>> setName:
static NSString  * setterForGetter(NSString *getter){
    
    if (getter.length <= 0) { return nil; }
    
    NSString *firstString = [[getter substringToIndex:1] uppercaseString];
    NSString *leaveString = [getter substringFromIndex:1];
    
    return [NSString stringWithFormat:@"set%@%@:",firstString,leaveString];
}

#pragma mark - 从set方法获取getter方法的名称 setName:===> name
static NSString * getterForSetter(NSString *setter){
    
    if (setter.length <= 0 || ![setter hasPrefix:@"set"] || ![setter hasSuffix:@":"]) { return nil;}
    
    NSRange range = NSMakeRange(3, setter.length-4);
    NSString *getter = [setter substringWithRange:range];
    NSString *firstString = [[getter substringToIndex:1] lowercaseString];
    getter = [getter stringByReplacingCharactersInRange:NSMakeRange(0, 1) withString:firstString];
    
    return getter;
}



@end
